﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.ComponentModel.DataAnnotations;

namespace Atacadao.Comum.DataAnnotation
{
    public class CpfCnpjValidator : ValidationAttribute, IClientValidatable
    {
        private TipoValidacaoCPFCNPJ tipo;

        public TipoValidacaoCPFCNPJ Tipo
        {
            get { return tipo; }
            set { tipo = value; }
        }

        public CpfCnpjValidator(TipoValidacaoCPFCNPJ tipo)
        {
            this.tipo = tipo;
        }

        public override string FormatErrorMessage(string name)
        {
            return base.FormatErrorMessage(name);
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            ModelClientValidationRule rule = new ModelClientValidationRule();
            rule.ValidationParameters.Add("tipo", tipo.ToString());
            rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
            rule.ValidationType = "cpfcnpj";

            yield return rule;
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            ValidationResult retorno = null;

            switch (tipo)
	        {
		        case TipoValidacaoCPFCNPJ.CPF:
                    if(!this.ValidaCPF(value.ToString())) retorno = new ValidationResult("CPF inválido");
                 break;
                case TipoValidacaoCPFCNPJ.CNPJ:
                    if(this.ValidaCNPJ(value.ToString())) retorno = new ValidationResult("CNPJ inválido");
                 break;
                case TipoValidacaoCPFCNPJ.CPF_CNPJ:
                    string aux = value.ToString();
                    if(aux.Length == 11){
                        if(!this.ValidaCPF(value.ToString())) retorno = new ValidationResult("CPF inválido");
                    }
                    else if(aux.Length > 11){
                        if(this.ValidaCNPJ(value.ToString())) retorno = new ValidationResult("CNPJ inválido");
                    }
                    else retorno = retorno = new ValidationResult("CNPJ ou CNPJ com formato inválido");
                 break;
                default:
                    throw new InvalidOperationException("Tipo indefinido");
	        }

            if (retorno == null) retorno = ValidationResult.Success;

            return retorno;
        }

        #region "Método de valição"
        public bool ValidaCNPJ(string vrCNPJ)
        {

            string CNPJ = vrCNPJ.Replace(".", "");

            CNPJ = CNPJ.Replace("/", "");

            CNPJ = CNPJ.Replace("-", "");



            int[] digitos, soma, resultado;

            int nrDig;

            string ftmt;

            bool[] CNPJOk;



            ftmt = "6543298765432";

            digitos = new int[14];

            soma = new int[2];

            soma[0] = 0;

            soma[1] = 0;

            resultado = new int[2];

            resultado[0] = 0;

            resultado[1] = 0;

            CNPJOk = new bool[2];

            CNPJOk[0] = false;

            CNPJOk[1] = false;



            try
            {

                for (nrDig = 0; nrDig < 14; nrDig++)
                {

                    digitos[nrDig] = int.Parse(

                        CNPJ.Substring(nrDig, 1));

                    if (nrDig <= 11)

                        soma[0] += (digitos[nrDig] *

                          int.Parse(ftmt.Substring(

                          nrDig + 1, 1)));

                    if (nrDig <= 12)

                        soma[1] += (digitos[nrDig] *

                          int.Parse(ftmt.Substring(

                          nrDig, 1)));

                }



                for (nrDig = 0; nrDig < 2; nrDig++)
                {

                    resultado[nrDig] = (soma[nrDig] % 11);

                    if ((resultado[nrDig] == 0) || (

                         resultado[nrDig] == 1))

                        CNPJOk[nrDig] = (

                        digitos[12 + nrDig] == 0);

                    else

                        CNPJOk[nrDig] = (

                        digitos[12 + nrDig] == (

                        11 - resultado[nrDig]));

                }

                return (CNPJOk[0] && CNPJOk[1]);

            }

            catch
            {

                return false;

            }

        }

        public bool ValidaCPF(string vrCPF)
        {

            string valor = vrCPF.Replace(".", "");

            valor = valor.Replace("-", "");



            if (valor.Length != 11)

                return false;



            bool igual = true;

            for (int i = 1; i < 11 && igual; i++)

                if (valor[i] != valor[0])

                    igual = false;



            if (igual || valor == "12345678909")

                return false;



            int[] numeros = new int[11];



            for (int i = 0; i < 11; i++)

                numeros[i] = int.Parse(

                  valor[i].ToString());



            int soma = 0;

            for (int i = 0; i < 9; i++)

                soma += (10 - i) * numeros[i];



            int resultado = soma % 11;



            if (resultado == 1 || resultado == 0)
            {

                if (numeros[9] != 0)

                    return false;

            }

            else if (numeros[9] != 11 - resultado)

                return false;



            soma = 0;

            for (int i = 0; i < 10; i++)

                soma += (11 - i) * numeros[i];



            resultado = soma % 11;



            if (resultado == 1 || resultado == 0)
            {

                if (numeros[10] != 0)

                    return false;

            }

            else

                if (numeros[10] != 11 - resultado)

                    return false;



            return true;

        }
        #endregion



    }
}
